package dao

import (
	"context"
	"time"

	"gitee.com/youkelike/zmigrate/migrator"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

var ErrDataNotFound = gorm.ErrRecordNotFound

//go:generate mockgen -source=./interactive.go -package=daomocks -destination=mocks/interactive.mock.go InteractiveDAO
type InteractiveDAO interface {
	IncrReadCnt(ctx context.Context, biz string, bizId int64) error
	InsertLikeInfo(ctx context.Context, biz string, bizId, uid int64) error
}

type GORMInteractiveDAO struct {
	db *gorm.DB
}

func (dao *GORMInteractiveDAO) InsertLikeInfo(ctx context.Context, biz string, bizId, uid int64) error {
	now := time.Now().UnixMilli()
	err := dao.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		err := tx.Clauses(clause.OnConflict{
			DoUpdates: clause.Assignments(map[string]any{
				"status": 1,
				"utime":  now,
			}),
		}).Create(&UserLikeBiz{
			Uid:    uid,
			Ctime:  now,
			Utime:  now,
			Biz:    biz,
			BizId:  bizId,
			Status: 1,
		}).Error
		if err != nil {
			return err
		}
		return tx.Clauses(clause.OnConflict{
			DoUpdates: clause.Assignments(map[string]any{
				"like_cnt": gorm.Expr("`like_cnt`+1"),
				"utime":    now,
			}),
		}).Create(&Interactive{
			LikeCnt: 1,
			Ctime:   now,
			Utime:   now,
			Biz:     biz,
			BizId:   bizId,
		}).Error
	})
	return err
}

func NewGORMInteractiveDAO(db *gorm.DB) InteractiveDAO {
	return &GORMInteractiveDAO{
		db: db,
	}
}

// IncrReadCnt 是一个插入或者更新语义
func (dao *GORMInteractiveDAO) IncrReadCnt(ctx context.Context, biz string, bizId int64) error {
	return dao.incrReadCnt(dao.db.WithContext(ctx), biz, bizId)
}

func (dao *GORMInteractiveDAO) incrReadCnt(tx *gorm.DB, biz string, bizId int64) error {
	now := time.Now().UnixMilli()
	return tx.Clauses(clause.OnConflict{
		DoUpdates: clause.Assignments(map[string]any{
			"read_cnt": gorm.Expr("`read_cnt`+1"),
			"utime":    now,
		}),
	}).Create(&Interactive{
		ReadCnt: 1,
		Ctime:   now,
		Utime:   now,
		Biz:     biz,
		BizId:   bizId,
	}).Error
}

// 必须用 mapstructure tags 指定字段名才能正确解析 canal 消息中的数据
type Interactive struct {
	Id         int64  `gorm:"primaryKey,autoIncrement"`
	BizId      int64  `gorm:"uniqueIndex:biz_type_id" mapstructure:"biz_id"`
	Biz        string `gorm:"type:varchar(128);uniqueIndex:biz_type_id" mapstructure:"biz"`
	ReadCnt    int64  `mapstructure:"read_cnt"`
	CollectCnt int64  `mapstructure:"collect_cnt"`
	LikeCnt    int64  `mapstructure:"like_cnt"`
	CommentCnt int64  `mapstructure:"comment_cnt"`
	Ctime      int64  `mapstructure:"ctime"`
	Utime      int64  `mapstructure:"utime"`
}

func (i Interactive) ID() int64 {
	return i.Id
}

func (i Interactive) CompareTo(dst migrator.Entity) bool {
	dstVal, ok := dst.(Interactive)
	return ok && i == dstVal
}

func (i Interactive) TableName() string {
	return "interactives"
}

type UserLikeBiz struct {
	Id int64 `gorm:"primaryKey,autoIncrement" mapstructure:"id"`
	// 三个构成唯一索引
	BizId int64  `gorm:"uniqueIndex:biz_type_id_uid" mapstructure:"biz_id"`
	Biz   string `gorm:"type:varchar(128);uniqueIndex:biz_type_id_uid" mapstructure:"biz"`
	Uid   int64  `gorm:"uniqueIndex:biz_type_id_uid" mapstructure:"uid"`
	// 依旧是只在 DB 层面生效的状态
	// 1- 有效，0-无效。软删除的用法
	Status uint8 `mapstructure:"status"`
	Ctime  int64 `mapstructure:"ctime"`
	Utime  int64 `mapstructure:"utime"`
}

func (i UserLikeBiz) ID() int64 {
	return i.Id
}

func (i UserLikeBiz) CompareTo(dst migrator.Entity) bool {
	dstVal, ok := dst.(UserLikeBiz)
	return ok && i == dstVal
}

func (i UserLikeBiz) TableName() string {
	return "user_like_bizs"
}
